package net.fdukedom.epicurus.domain.util;

import net.fdukedom.epicurus.client.plugin.ClientPluginInterface;
import net.fdukedom.epicurus.domain.entity.Extension;
import net.fdukedom.epicurus.domain.entity.Plugin;
import net.fdukedom.epicurus.messaging.MessageRecipient;
import net.fdukedom.epicurus.resource.ExtensionHandler;
import net.fdukedom.epicurus.tools.logging.Logger;
import net.fdukedom.epicurus.tools.logging.WriterId;
import net.fdukedom.epicurus.service.WSMessageHandlerInterface;
import org.hibernate.Criteria;
import org.hibernate.Session;

import java.util.HashMap;
import java.util.Map;

/**
 * Utility class for Plugi-ns' routine.
 *
 * @author Dmitry Y. Kochelaev
 * @author Sergey Vishnyakov
 */
public final class PluginHelper {

    /**
     * The only instance of the class.
     */
    private static final PluginHelper instance = new PluginHelper();

    private Logger logger = Logger.getInstance(WriterId.DOMAIN);

    /**
     * Default constructor.
     */
    private PluginHelper() {
        //this is an utility class only one instance is allowed
    }

    /**
     * Returns the only instance of the class.
     *
     * @return the only instance of the class
     */
    public static PluginHelper getInstance() {
        return instance;
    }

    /**
     * Saves plugin record in database.
     *
     * @param plugin plugin to save
     */
    public synchronized void savePlugin(Plugin plugin) {
        Session session = HibernateUtil.getSession();
        session.saveOrUpdate(plugin);
        logger.logInfo("Plugin was saved: " + plugin);
    }

    /**
     * Loads map of registred plugins from database.
     *
     * @return map of registred plugins from database.
     */
    public Map<String, Plugin> loadPluginsMap() {
        Session session = HibernateUtil.getSession();
        Criteria criteria = session.createCriteria(Plugin.class);
        Map<String, Plugin> plugins = new HashMap<String, Plugin>();
        for (Object item : criteria.list()) {
            Plugin plugin = (Plugin) item;
            plugins.put(plugin.getName(), plugin);
        }
        logger.logDebug("Plugins map loaded. Plugins count: " + plugins.size());
        return plugins;
    }

    /**
     * Creates an instance of MessageRecipient for plug-in.
     *
     * @param plugin plug-in, which recipient is needed.
     *
     * @return recipient
     *
     * @throws DomainException if an error occured
     */
    public MessageRecipient getMessageRecipient(Plugin plugin) throws DomainException {
        try {
            return (MessageRecipient) Class.forName(plugin.getRecipient()).newInstance();
        } catch (ClassNotFoundException e) {
            throw new DomainException(e);
        } catch (IllegalAccessException e) {
            throw new DomainException(e);
        } catch (InstantiationException e) {
            throw new DomainException(e);
        }
    }

    /**
     * Creates instance of Extension for point.
     *
     * @param extension extension
     *
     * @return extension handler
     *
     * @throws DomainException if an error occured
     */
    public ExtensionHandler getExtension(Extension extension) throws DomainException {
        try {
            ExtensionHandler handler = (ExtensionHandler) Class.forName(extension.getClassName()).newInstance();
            handler.setExtension(extension);
            return handler;
        } catch (ClassNotFoundException e) {
            throw new DomainException(e);
        } catch (IllegalAccessException e) {
            throw new DomainException(e);
        } catch (InstantiationException e) {
            throw new DomainException(e);
        }
    }

    /**
     * Creates instance of Client plugin for plug-in.
     *
     * @param plugin plug-in
     *
     * @return client plugin implementation
     *
     * @throws DomainException if an error occured
     */
    public ClientPluginInterface getClientPlugin(Plugin plugin) throws DomainException {
        try {
            return (ClientPluginInterface) Class.forName(plugin.getClientPlugin()).newInstance();
        } catch (ClassNotFoundException e) {
            throw new DomainException(e);
        } catch (IllegalAccessException e) {
            throw new DomainException(e);
        } catch (InstantiationException e) {
            throw new DomainException(e);
        }
    }

    /**
     * Creates instance of ws-endpoint for plug-in.
     *
     * @param plugin plug-in
     *
     * @return ws-endpoint implementation
     *
     * @throws DomainException if an error occured
     */
    public WSMessageHandlerInterface getWsEndpointPlugin(Plugin plugin) throws DomainException {
        try {
            return (WSMessageHandlerInterface) Class.forName(plugin.getWsEndpoint()).newInstance();
        } catch (ClassNotFoundException e) {
            throw new DomainException(e);
        } catch (IllegalAccessException e) {
            throw new DomainException(e);
        } catch (InstantiationException e) {
            throw new DomainException(e);
        }
    }
}
